@prosekit/extensions 0.11.5 → 0.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/drop-indicator-E7nCfdnR.js +58 -0
  2. package/dist/drop-indicator-E7nCfdnR.js.map +1 -0
  3. package/dist/{enter-rule-RdhEA900.js → enter-rule-5tkoU2Ir.js} +2 -3
  4. package/dist/enter-rule-5tkoU2Ir.js.map +1 -0
  5. package/dist/file-nRyo7PMB.js +132 -0
  6. package/dist/file-nRyo7PMB.js.map +1 -0
  7. package/dist/index-DY6lIIYV.d.ts +134 -0
  8. package/dist/index-DY6lIIYV.d.ts.map +1 -0
  9. package/dist/{input-rule-B17tpW4m.js → input-rule-DO_iy2aT.js} +4 -7
  10. package/dist/input-rule-DO_iy2aT.js.map +1 -0
  11. package/dist/{mark-rule-BCqIZMDu.js → mark-rule-CGmswjQ_.js} +1 -1
  12. package/dist/{mark-rule-BCqIZMDu.js.map → mark-rule-CGmswjQ_.js.map} +1 -1
  13. package/dist/{paste-rule-DIEJKIje.js → paste-rule-Brej6cWi.js} +2 -3
  14. package/dist/{paste-rule-DIEJKIje.js.map → paste-rule-Brej6cWi.js.map} +1 -1
  15. package/dist/prosekit-extensions-autocomplete.js +1 -2
  16. package/dist/prosekit-extensions-autocomplete.js.map +1 -1
  17. package/dist/prosekit-extensions-blockquote.js +1 -1
  18. package/dist/prosekit-extensions-bold.js +1 -1
  19. package/dist/prosekit-extensions-code-block.d.ts +1 -1
  20. package/dist/prosekit-extensions-code-block.js +4 -5
  21. package/dist/prosekit-extensions-code-block.js.map +1 -1
  22. package/dist/prosekit-extensions-code.js +1 -1
  23. package/dist/prosekit-extensions-commit.js +2 -4
  24. package/dist/prosekit-extensions-commit.js.map +1 -1
  25. package/dist/prosekit-extensions-drop-indicator.d.ts +3 -106
  26. package/dist/prosekit-extensions-drop-indicator.d.ts.map +1 -1
  27. package/dist/prosekit-extensions-drop-indicator.js +1 -1
  28. package/dist/prosekit-extensions-enter-rule.js +1 -1
  29. package/dist/prosekit-extensions-file.d.ts +2 -126
  30. package/dist/prosekit-extensions-file.js +2 -128
  31. package/dist/prosekit-extensions-hard-break.d.ts +0 -4
  32. package/dist/prosekit-extensions-hard-break.d.ts.map +1 -1
  33. package/dist/prosekit-extensions-heading.js +1 -1
  34. package/dist/prosekit-extensions-horizontal-rule.js +1 -1
  35. package/dist/prosekit-extensions-image.d.ts +135 -3
  36. package/dist/prosekit-extensions-image.d.ts.map +1 -1
  37. package/dist/prosekit-extensions-image.js +129 -8
  38. package/dist/prosekit-extensions-image.js.map +1 -1
  39. package/dist/prosekit-extensions-input-rule.js +1 -1
  40. package/dist/prosekit-extensions-italic.js +1 -1
  41. package/dist/prosekit-extensions-link.js +4 -4
  42. package/dist/prosekit-extensions-list.js +5 -7
  43. package/dist/prosekit-extensions-list.js.map +1 -1
  44. package/dist/prosekit-extensions-mark-rule.js +1 -1
  45. package/dist/prosekit-extensions-paragraph.d.ts +0 -4
  46. package/dist/prosekit-extensions-paragraph.d.ts.map +1 -1
  47. package/dist/prosekit-extensions-paste-rule.js +1 -1
  48. package/dist/prosekit-extensions-placeholder.js +3 -4
  49. package/dist/prosekit-extensions-placeholder.js.map +1 -1
  50. package/dist/prosekit-extensions-search.js +1 -2
  51. package/dist/prosekit-extensions-search.js.map +1 -1
  52. package/dist/prosekit-extensions-strike.js +1 -1
  53. package/dist/prosekit-extensions-table.js +2 -2
  54. package/dist/prosekit-extensions.d.ts +1 -1
  55. package/dist/{shiki-highlighter-chunk-DSPM0T27.d.ts → shiki-highlighter-chunk-Cwu1Jr9o.d.ts} +1 -1
  56. package/dist/{shiki-highlighter-chunk-DSPM0T27.d.ts.map → shiki-highlighter-chunk-Cwu1Jr9o.d.ts.map} +1 -1
  57. package/dist/shiki-highlighter-chunk.d.ts +1 -1
  58. package/dist/{table-Bi7WsMI3.js → table-DND_1127.js} +10 -19
  59. package/dist/{table-Bi7WsMI3.js.map → table-DND_1127.js.map} +1 -1
  60. package/package.json +8 -7
  61. package/src/drop-indicator/drop-indicator-facet.ts +6 -28
  62. package/src/drop-indicator/drop-indicator.ts +3 -5
  63. package/src/drop-indicator/index.ts +6 -6
  64. package/src/file/file-upload.ts +19 -6
  65. package/src/image/image-commands/insert-image.ts +14 -0
  66. package/src/image/image-commands/upload-image.ts +137 -0
  67. package/src/image/image-commands.ts +8 -4
  68. package/src/image/image-upload-handler.ts +96 -0
  69. package/src/image/index.ts +14 -0
  70. package/dist/drop-indicator-D1eHOhSi.js +0 -267
  71. package/dist/drop-indicator-D1eHOhSi.js.map +0 -1
  72. package/dist/enter-rule-RdhEA900.js.map +0 -1
  73. package/dist/input-rule-B17tpW4m.js.map +0 -1
  74. package/dist/prosekit-extensions-file.d.ts.map +0 -1
  75. package/dist/prosekit-extensions-file.js.map +0 -1
  76. package/src/drop-indicator/drop-indicator-plugin.ts +0 -147
  77. package/src/drop-indicator/drop-target.ts +0 -168
  78. package/src/drop-indicator/types.ts +0 -90
@@ -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,9 +1,13 @@
1
1
  import {
2
2
  defineCommands,
3
- insertNode,
4
3
  type Extension,
5
4
  } from '@prosekit/core'
6
5
 
6
+ import { insertImage } from './image-commands/insert-image'
7
+ import {
8
+ uploadImage,
9
+ type UploadImageOptions,
10
+ } from './image-commands/upload-image'
7
11
  import type { ImageAttrs } from './image-spec'
8
12
 
9
13
  /**
@@ -12,6 +16,7 @@ import type { ImageAttrs } from './image-spec'
12
16
  export type ImageCommandsExtension = Extension<{
13
17
  Commands: {
14
18
  insertImage: [attrs?: ImageAttrs]
19
+ uploadImage: [options: UploadImageOptions]
15
20
  }
16
21
  }>
17
22
 
@@ -20,8 +25,7 @@ export type ImageCommandsExtension = Extension<{
20
25
  */
21
26
  export function defineImageCommands(): ImageCommandsExtension {
22
27
  return defineCommands({
23
- insertImage: (attrs?: ImageAttrs) => {
24
- return insertNode({ type: 'image', attrs })
25
- },
28
+ insertImage,
29
+ uploadImage,
26
30
  })
27
31
  }
@@ -0,0 +1,96 @@
1
+ import {
2
+ union,
3
+ type PlainExtension,
4
+ } from '@prosekit/core'
5
+
6
+ import {
7
+ defineFileDropHandler,
8
+ defineFilePasteHandler,
9
+ type FileDropHandler,
10
+ type FileDropHandlerOptions,
11
+ type FilePasteHandler,
12
+ type FilePasteHandlerOptions,
13
+ type Uploader,
14
+ } from '../file'
15
+
16
+ import {
17
+ uploadImage,
18
+ type ImageUploadErrorHandler,
19
+ } from './image-commands/upload-image'
20
+
21
+ /**
22
+ * A predicate to determine if the pasted file should be uploaded and inserted as an image.
23
+ */
24
+ export type ImageCanPastePredicate = (options: FilePasteHandlerOptions) => boolean
25
+
26
+ /**
27
+ * A predicate to determine if the dropped file should be uploaded and inserted as an image.
28
+ */
29
+ export type ImageCanDropPredicate = (options: FileDropHandlerOptions) => boolean
30
+
31
+ /**
32
+ * A handler to be called when an error occurs during the upload.
33
+ */
34
+ export interface ImageUploadHandlerOptions {
35
+ /**
36
+ * The uploader used to upload the file. It should return a promise that
37
+ * resolves to the URL of the uploaded image.
38
+ */
39
+ uploader: Uploader<string>
40
+ /**
41
+ * A predicate to determine if the pasted file should be uploaded and inserted as an image.
42
+ * If not provided, it defaults to only allowing paste of files with a content type starting with `image/`.
43
+ */
44
+ canPaste?: ImageCanPastePredicate
45
+ /**
46
+ * A predicate to determine if the dropped file should be uploaded and inserted as an image.
47
+ * If not provided, it defaults to only allowing drop of files with a content type starting with `image/`.
48
+ */
49
+ canDrop?: ImageCanDropPredicate
50
+ /**
51
+ * A handler to be called when an error occurs during the upload.
52
+ * If not provided, it defaults to logging the error to the console.
53
+ */
54
+ onError?: ImageUploadErrorHandler
55
+ }
56
+
57
+ function defaultCanUpload({ file }: { file: File }): boolean {
58
+ // Only handle image files by default
59
+ return file.type.startsWith('image/')
60
+ }
61
+
62
+ const defaultOnError: ImageUploadErrorHandler = ({ error }) => {
63
+ console.error('[prosekit] Failed to upload image:', error)
64
+ }
65
+
66
+ /**
67
+ * Returns an extension that handles image file uploads when pasting or dropping
68
+ * images into the editor.
69
+ *
70
+ * @param options
71
+ */
72
+ export function defineImageUploadHandler({
73
+ uploader,
74
+ canPaste = defaultCanUpload,
75
+ canDrop = defaultCanUpload,
76
+ onError = defaultOnError,
77
+ }: ImageUploadHandlerOptions): PlainExtension {
78
+ const handlePaste: FilePasteHandler = (options) => {
79
+ if (!canPaste(options)) return false
80
+ const { view, file } = options
81
+ const command = uploadImage({ uploader, file, onError })
82
+ return command(view.state, view.dispatch, view)
83
+ }
84
+
85
+ const handleDrop: FileDropHandler = (options) => {
86
+ if (!canDrop(options)) return false
87
+ const { view, file, pos } = options
88
+ const command = uploadImage({ uploader, file, onError, pos })
89
+ return command(view.state, view.dispatch, view)
90
+ }
91
+
92
+ return union(
93
+ defineFilePasteHandler(handlePaste),
94
+ defineFileDropHandler(handleDrop),
95
+ )
96
+ }
@@ -6,8 +6,22 @@ export {
6
6
  defineImageCommands,
7
7
  type ImageCommandsExtension,
8
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'
9
17
  export {
10
18
  defineImageSpec,
11
19
  type ImageAttrs,
12
20
  type ImageSpecExtension,
13
21
  } from './image-spec'
22
+ export {
23
+ defineImageUploadHandler,
24
+ type ImageCanDropPredicate,
25
+ type ImageCanPastePredicate,
26
+ type ImageUploadHandlerOptions,
27
+ } from './image-upload-handler'
@@ -1,267 +0,0 @@
1
- import { defineFacet, defineFacetPayload, isNodeSelection, pluginFacet } from "@prosekit/core";
2
- import { NodeSelection, Plugin, PluginKey, TextSelection } from "@prosekit/pm/state";
3
- import { isHTMLElement } from "@ocavue/utils";
4
-
5
- //#region src/drop-indicator/drop-target.ts
6
- function getTargetsByView(view) {
7
- let stack = [[-1, view.state.doc]];
8
- let targets = [];
9
- while (stack.length > 0) {
10
- const [pos, node] = stack.pop();
11
- if (pos >= 0) {
12
- let dom = view.nodeDOM(pos);
13
- if (dom && isHTMLElement(dom)) {
14
- let { top, bottom, left: x1, right: x2 } = dom.getBoundingClientRect();
15
- targets.push([pos, [
16
- x1,
17
- top,
18
- x2,
19
- top
20
- ]], [pos + node.nodeSize, [
21
- x1,
22
- bottom,
23
- x2,
24
- bottom
25
- ]]);
26
- }
27
- }
28
- if (node.isBlock && !node.isTextblock) {
29
- let childPos = pos + 1;
30
- for (let child of node.children) {
31
- stack.push([childPos, child]);
32
- childPos += child.nodeSize;
33
- }
34
- }
35
- }
36
- return targets;
37
- }
38
- /**
39
- * @internal
40
- */
41
- function buildGetTarget(view, onDrag) {
42
- let prevTargets = [];
43
- let prevDoc;
44
- let prevRect;
45
- const getTargets = () => {
46
- const rect = view.dom.getBoundingClientRect();
47
- const doc = view.state.doc;
48
- if (prevTargets && prevDoc && prevRect && rect.width === prevRect.width && rect.height === prevRect.height && rect.x === prevRect.x && rect.y === prevRect.y && prevDoc.eq(doc)) return prevTargets;
49
- prevRect = rect;
50
- prevDoc = doc;
51
- prevTargets = getTargetsByView(view);
52
- return prevTargets;
53
- };
54
- const getTargetImpl = (point, event) => {
55
- if (!view.editable || view.isDestroyed) return;
56
- const compare = (p1, p2) => {
57
- const [pos1, line1] = p1;
58
- const [pos2, line2] = p2;
59
- const p1Distance = pointLineDistance(point, line1);
60
- const p2Distance = pointLineDistance(point, line2);
61
- return p1Distance - p2Distance || pos1 - pos2;
62
- };
63
- let targets = getTargets();
64
- targets.sort(compare);
65
- targets = targets.slice(0, 8);
66
- const target = targets.find((target$1) => onDrag({
67
- view,
68
- pos: target$1[0],
69
- event
70
- }) !== false);
71
- if (target && isDraggingToItself(view, target[0])) return;
72
- return target;
73
- };
74
- let prevPoint;
75
- let prevTarget;
76
- const getTargetCached = (point, event) => {
77
- if (prevPoint && pointEqual(prevPoint, point)) return prevTarget;
78
- prevPoint = point;
79
- prevTarget = getTargetImpl(point, event);
80
- return prevTarget;
81
- };
82
- return getTargetCached;
83
- }
84
- function pointEqual(a, b) {
85
- return a[0] === b[0] && a[1] === b[1];
86
- }
87
- function pointPointDistance(a, b) {
88
- return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);
89
- }
90
- function pointLineDistance(point, line) {
91
- return Math.min(pointPointDistance(point, [line[0], line[1]]), pointPointDistance(point, [line[2], line[3]]));
92
- }
93
- /**
94
- * Whether the dragging node is being dragged to the same position. For example,
95
- * dragging a list node into a new position that is just below the list node, or
96
- * dragging a nested quoteblock into itself.
97
- */
98
- function isDraggingToItself(view, pos) {
99
- const dragging = view.dragging;
100
- if (!dragging) return;
101
- const { move } = dragging;
102
- if (!move) return;
103
- const selection = view.state.selection;
104
- if (!isNodeSelection(selection)) return;
105
- const { from, to } = selection;
106
- return from <= pos && pos <= to;
107
- }
108
-
109
- //#endregion
110
- //#region src/drop-indicator/drop-indicator-plugin.ts
111
- /**
112
- * @internal
113
- */
114
- function createDropIndicatorPlugin(options) {
115
- let getTarget;
116
- return new Plugin({
117
- key: new PluginKey("prosekit-drop-indicator"),
118
- view: (view) => {
119
- getTarget = buildGetTarget(view, options.onDrag);
120
- return createDropIndicatorView(view, getTarget, options);
121
- },
122
- props: { handleDrop(view, event, slice, move) {
123
- if (!getTarget) return false;
124
- const target = getTarget([event.clientX, event.clientY], event);
125
- if (!target) return false;
126
- event.preventDefault();
127
- let insertPos = target[0];
128
- let tr = view.state.tr;
129
- if (move) {
130
- let { node } = view.dragging || {};
131
- if (node) node.replace(tr);
132
- else tr.deleteSelection();
133
- }
134
- let pos = tr.mapping.map(insertPos);
135
- let isNode = slice.openStart == 0 && slice.openEnd == 0 && slice.content.childCount == 1;
136
- let beforeInsert = tr.doc;
137
- if (isNode) tr.replaceRangeWith(pos, pos, slice.content.firstChild);
138
- else tr.replaceRange(pos, pos, slice);
139
- if (tr.doc.eq(beforeInsert)) return true;
140
- let $pos = tr.doc.resolve(pos);
141
- if (isNode && NodeSelection.isSelectable(slice.content.firstChild) && $pos.nodeAfter && $pos.nodeAfter.sameMarkup(slice.content.firstChild)) tr.setSelection(new NodeSelection($pos));
142
- else {
143
- let end = tr.mapping.map(insertPos);
144
- tr.mapping.maps[tr.mapping.maps.length - 1].forEach((_from, _to, _newFrom, newTo) => end = newTo);
145
- tr.setSelection(selectionBetween(view, $pos, tr.doc.resolve(end)));
146
- }
147
- view.focus();
148
- view.dispatch(tr.setMeta("uiEvent", "drop"));
149
- return true;
150
- } }
151
- });
152
- }
153
- function selectionBetween(view, $anchor, $head, bias) {
154
- return view.someProp("createSelectionBetween", (f) => f(view, $anchor, $head)) || TextSelection.between($anchor, $head, bias);
155
- }
156
- function createDropIndicatorView(view, getTarget, options) {
157
- let dom = view.dom;
158
- let hideId;
159
- let prevX;
160
- let prevY;
161
- let hasDragOverEvent = false;
162
- const scheduleHide = () => {
163
- if (hideId) clearTimeout(hideId);
164
- hasDragOverEvent = false;
165
- hideId = setTimeout(() => {
166
- if (hasDragOverEvent) return;
167
- options.onHide();
168
- }, 30);
169
- };
170
- const handleDragOver = (event) => {
171
- hasDragOverEvent = true;
172
- const { clientX, clientY } = event;
173
- if (prevX === clientX && prevY === clientY) return;
174
- prevX = clientX;
175
- prevY = clientY;
176
- let target = getTarget([clientX, clientY], event);
177
- if (!target) {
178
- scheduleHide();
179
- return;
180
- } else {
181
- const [pos, [x1, y1, x2, y2]] = target;
182
- const line = {
183
- p1: {
184
- x: x1,
185
- y: y1
186
- },
187
- p2: {
188
- x: x2,
189
- y: y2
190
- }
191
- };
192
- options.onShow({
193
- view,
194
- pos,
195
- line
196
- });
197
- }
198
- };
199
- dom.addEventListener("dragover", handleDragOver);
200
- dom.addEventListener("dragend", scheduleHide);
201
- dom.addEventListener("drop", scheduleHide);
202
- dom.addEventListener("dragleave", scheduleHide);
203
- const destroy = () => {
204
- dom.removeEventListener("dragover", handleDragOver);
205
- dom.removeEventListener("dragend", scheduleHide);
206
- dom.removeEventListener("drop", scheduleHide);
207
- dom.removeEventListener("dragleave", scheduleHide);
208
- };
209
- return { destroy };
210
- }
211
-
212
- //#endregion
213
- //#region src/drop-indicator/drop-indicator-facet.ts
214
- /**
215
- * @internal
216
- */
217
- function defineDropIndicatorPayload(payload) {
218
- return defineFacetPayload(dropIndicatorFacet, [payload]);
219
- }
220
- const dropIndicatorFacet = defineFacet({
221
- parent: pluginFacet,
222
- singleton: true,
223
- reducer: (payloads) => {
224
- let showHandlers = payloads.map((p) => p.onShow).filter((x) => !!x);
225
- let hideHandlers = payloads.map((p) => p.onHide).filter((x) => !!x);
226
- let dragHandlers = payloads.map((p) => p.onDrag).filter((x) => !!x);
227
- let showHandler = (options) => {
228
- for (let fn of showHandlers) fn(options);
229
- };
230
- let hideHandler = () => {
231
- for (let fn of hideHandlers) fn();
232
- };
233
- let dragHandler = (options) => {
234
- for (let fn of dragHandlers) if (fn(options) === false) return false;
235
- return true;
236
- };
237
- if (showHandlers.length === 0) return [];
238
- return createDropIndicatorPlugin({
239
- onDrag: dragHandler,
240
- onShow: showHandler,
241
- onHide: hideHandler
242
- });
243
- }
244
- });
245
-
246
- //#endregion
247
- //#region src/drop-indicator/drop-indicator.ts
248
- /**
249
- * Defines an extension that controls the behavior of the drop indicator.
250
- *
251
- * This extension itself doesn't draw the drop indicator, but it provides the
252
- * necessary callbacks to do so. You probably don't want to use this extension
253
- * directly, but rather use the `<DropIndicator>` component.
254
- *
255
- * You can add this extension multiple times. If any extension has `onDrag`
256
- * callback defined, and it returns `false`, then the drop point will be
257
- * discarded.
258
- *
259
- * @public
260
- */
261
- function defineDropIndicator(options) {
262
- return defineDropIndicatorPayload(options ?? {});
263
- }
264
-
265
- //#endregion
266
- export { defineDropIndicator };
267
- //# sourceMappingURL=drop-indicator-D1eHOhSi.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"drop-indicator-D1eHOhSi.js","names":["stack: StackItem[]","targets: DropTarget[]","prevTargets: DropTarget[]","prevDoc: ProseMirrorNode | undefined","prevRect: DOMRect | undefined","getTargetImpl: GetTarget","target","prevPoint: Point | undefined","prevTarget: DropTarget | undefined","getTargetCached: GetTarget","getTarget: GetTarget | undefined","hideId: ReturnType<typeof setTimeout> | undefined","prevX: number | undefined","prevY: number | undefined","hasDragOverEvent: boolean","showHandler: ShowHandler","hideHandler: VoidFunction","dragHandler: DragEventHandler"],"sources":["../src/drop-indicator/drop-target.ts","../src/drop-indicator/drop-indicator-plugin.ts","../src/drop-indicator/drop-indicator-facet.ts","../src/drop-indicator/drop-indicator.ts"],"sourcesContent":["import { isHTMLElement } from '@ocavue/utils'\nimport { isNodeSelection } from '@prosekit/core'\nimport type { ProseMirrorNode } from '@prosekit/pm/model'\nimport type { EditorView } from '@prosekit/pm/view'\n\nimport type { DragEventHandler } from './types'\n\ntype Point = readonly [x: number, y: number]\n\ntype Line = readonly [x1: number, y1: number, x2: number, y2: number]\n\n/**\n * @internal\n */\ntype DropTarget = readonly [pos: number, line: Line]\n\nfunction getTargetsByView(view: EditorView): DropTarget[] {\n type StackItem = [pos: number, node: ProseMirrorNode]\n let stack: StackItem[] = [[-1, view.state.doc]]\n let targets: DropTarget[] = []\n\n while (stack.length > 0) {\n const [pos, node] = stack.pop()!\n if (pos >= 0) {\n let dom = view.nodeDOM(pos)\n if (dom && isHTMLElement(dom)) {\n let rect = dom.getBoundingClientRect()\n let { top, bottom, left: x1, right: x2 } = rect\n targets.push(\n [pos, [x1, top, x2, top]],\n [pos + node.nodeSize, [x1, bottom, x2, bottom]],\n )\n }\n }\n if (node.isBlock && !node.isTextblock) {\n let childPos = pos + 1\n for (let child of node.children) {\n stack.push([childPos, child])\n childPos += child.nodeSize\n }\n }\n }\n\n return targets\n}\n\n/**\n * @internal\n */\nexport type GetTarget = (point: Point, event: DragEvent) => DropTarget | undefined\n\n/**\n * @internal\n */\nexport function buildGetTarget(\n view: EditorView,\n onDrag: DragEventHandler,\n): GetTarget {\n let prevTargets: DropTarget[] = []\n let prevDoc: ProseMirrorNode | undefined\n let prevRect: DOMRect | undefined\n\n const getTargets = (): DropTarget[] => {\n const rect = view.dom.getBoundingClientRect()\n const doc = view.state.doc\n\n if (\n prevTargets && prevDoc && prevRect\n && rect.width === prevRect.width\n && rect.height === prevRect.height\n && rect.x === prevRect.x\n && rect.y === prevRect.y\n && prevDoc.eq(doc)\n ) {\n return prevTargets\n }\n\n prevRect = rect\n prevDoc = doc\n prevTargets = getTargetsByView(view)\n return prevTargets\n }\n\n const getTargetImpl: GetTarget = (point, event) => {\n if (!view.editable || view.isDestroyed) {\n return\n }\n\n const compare = (p1: DropTarget, p2: DropTarget): number => {\n const [pos1, line1] = p1\n const [pos2, line2] = p2\n const p1Distance = pointLineDistance(point, line1)\n const p2Distance = pointLineDistance(point, line2)\n\n return (p1Distance - p2Distance) || (pos1 - pos2)\n }\n\n let targets = getTargets()\n targets.sort(compare)\n\n // Only consider the first few targets for performance reasons.\n targets = targets.slice(0, 8)\n\n // Find the closest valid target.\n const target = targets.find(target => onDrag({ view, pos: target[0], event }) !== false)\n\n // If the dragging node is already at the target position, we ignore this\n // target. Notice that we don't pick the second better target here.\n if (target && isDraggingToItself(view, target[0])) {\n return undefined\n }\n\n return target\n }\n\n let prevPoint: Point | undefined\n let prevTarget: DropTarget | undefined\n\n const getTargetCached: GetTarget = (point, event) => {\n if (prevPoint && pointEqual(prevPoint, point)) {\n return prevTarget\n }\n\n prevPoint = point\n prevTarget = getTargetImpl(point, event)\n return prevTarget\n }\n\n return getTargetCached\n}\n\nfunction pointEqual(a: Point, b: Point) {\n return a[0] === b[0] && a[1] === b[1]\n}\n\nfunction pointPointDistance(a: Point, b: Point) {\n // Manhattan distance\n return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1])\n}\n\nfunction pointLineDistance(point: Point, line: Line) {\n // Notice that we are actually not calculating the distance between the point\n // and the line. Instead, we are calculating the distance between the point\n // and the two endpoints of the line.\n return Math.min(\n pointPointDistance(point, [line[0], line[1]]),\n pointPointDistance(point, [line[2], line[3]]),\n )\n}\n\n/**\n * Whether the dragging node is being dragged to the same position. For example,\n * dragging a list node into a new position that is just below the list node, or\n * dragging a nested quoteblock into itself.\n */\nfunction isDraggingToItself(view: EditorView, pos: number) {\n const dragging = view.dragging\n if (!dragging) return\n\n const { move } = dragging\n if (!move) return\n\n const selection = view.state.selection\n if (!isNodeSelection(selection)) return\n\n const { from, to } = selection\n return from <= pos && pos <= to\n}\n","import type { ResolvedPos } from '@prosekit/pm/model'\nimport {\n NodeSelection,\n Plugin,\n PluginKey,\n TextSelection,\n type PluginView,\n} from '@prosekit/pm/state'\nimport type { EditorView } from '@prosekit/pm/view'\n\nimport {\n buildGetTarget,\n type GetTarget,\n} from './drop-target'\nimport type {\n DragEventHandler,\n ShowHandler,\n ViewDragging,\n} from './types'\n\n/**\n * @internal\n */\ninterface DropIndicatorPluginOptions {\n onDrag: DragEventHandler\n onShow: ShowHandler\n onHide: VoidFunction\n}\n\n/**\n * @internal\n */\nexport function createDropIndicatorPlugin(options: DropIndicatorPluginOptions): Plugin {\n let getTarget: GetTarget | undefined\n\n return new Plugin({\n key: new PluginKey('prosekit-drop-indicator'),\n view: (view) => {\n getTarget = buildGetTarget(view, options.onDrag)\n return createDropIndicatorView(view, getTarget, options)\n },\n props: {\n handleDrop(view, event, slice, move): boolean {\n if (!getTarget) return false\n\n const target = getTarget([event.clientX, event.clientY], event)\n\n if (!target) return false\n\n event.preventDefault()\n let insertPos = target[0]\n\n let tr = view.state.tr\n if (move) {\n let { node } = (view.dragging as ViewDragging | null) || {}\n if (node) node.replace(tr)\n else tr.deleteSelection()\n }\n\n let pos = tr.mapping.map(insertPos)\n let isNode = slice.openStart == 0 && slice.openEnd == 0 && slice.content.childCount == 1\n let beforeInsert = tr.doc\n if (isNode) tr.replaceRangeWith(pos, pos, slice.content.firstChild!)\n else tr.replaceRange(pos, pos, slice)\n if (tr.doc.eq(beforeInsert)) {\n return true\n }\n\n let $pos = tr.doc.resolve(pos)\n if (\n isNode && NodeSelection.isSelectable(slice.content.firstChild!)\n && $pos.nodeAfter && $pos.nodeAfter.sameMarkup(slice.content.firstChild!)\n ) {\n tr.setSelection(new NodeSelection($pos))\n } else {\n let end = tr.mapping.map(insertPos)\n tr.mapping.maps[tr.mapping.maps.length - 1].forEach((_from, _to, _newFrom, newTo) => end = newTo)\n tr.setSelection(selectionBetween(view, $pos, tr.doc.resolve(end)))\n }\n view.focus()\n view.dispatch(tr.setMeta('uiEvent', 'drop'))\n return true\n },\n },\n })\n}\n\nfunction selectionBetween(view: EditorView, $anchor: ResolvedPos, $head: ResolvedPos, bias?: number) {\n return view.someProp('createSelectionBetween', f => f(view, $anchor, $head))\n || TextSelection.between($anchor, $head, bias)\n}\n\nfunction createDropIndicatorView(view: EditorView, getTarget: GetTarget, options: DropIndicatorPluginOptions): PluginView {\n let dom = view.dom\n let hideId: ReturnType<typeof setTimeout> | undefined\n let prevX: number | undefined\n let prevY: number | undefined\n let hasDragOverEvent: boolean = false\n\n const scheduleHide = () => {\n if (hideId) {\n clearTimeout(hideId)\n }\n\n hasDragOverEvent = false\n hideId = setTimeout(() => {\n if (hasDragOverEvent) return\n options.onHide()\n }, 30)\n }\n\n const handleDragOver = (event: DragEvent): void => {\n hasDragOverEvent = true\n\n const { clientX, clientY } = event\n if (prevX === clientX && prevY === clientY) {\n return\n }\n prevX = clientX\n prevY = clientY\n\n let target = getTarget([clientX, clientY], event)\n\n if (!target) {\n scheduleHide()\n return\n } else {\n const [pos, [x1, y1, x2, y2]] = target\n const line = { p1: { x: x1, y: y1 }, p2: { x: x2, y: y2 } }\n options.onShow({ view, pos, line })\n }\n }\n\n dom.addEventListener('dragover', handleDragOver)\n dom.addEventListener('dragend', scheduleHide)\n dom.addEventListener('drop', scheduleHide)\n dom.addEventListener('dragleave', scheduleHide)\n\n const destroy = () => {\n dom.removeEventListener('dragover', handleDragOver)\n dom.removeEventListener('dragend', scheduleHide)\n dom.removeEventListener('drop', scheduleHide)\n dom.removeEventListener('dragleave', scheduleHide)\n }\n\n return { destroy }\n}\n","import {\n defineFacet,\n defineFacetPayload,\n pluginFacet,\n type PlainExtension,\n type PluginPayload,\n} from '@prosekit/core'\n\nimport { createDropIndicatorPlugin } from './drop-indicator-plugin'\nimport type {\n DragEventHandler,\n ShowHandler,\n} from './types'\n\n/**\n * @internal\n */\nexport function defineDropIndicatorPayload(\n payload: DropIndicatorPayload,\n): PlainExtension {\n return defineFacetPayload(dropIndicatorFacet, [payload]) as PlainExtension\n}\n\n/**\n * @internal\n */\nexport interface DropIndicatorPayload {\n /**\n * A callback that is called when the drop indicator should be shown.\n */\n onShow?: ShowHandler\n\n /**\n * A callback that is called when the drop indicator should be hidden.\n */\n onHide?: VoidFunction\n\n /**\n * A callback that is called when the `dragover` event is fired. You can\n * return `false` to disable the current drop point and thus hide the drop\n * indicator.\n */\n onDrag?: DragEventHandler\n}\n\nconst dropIndicatorFacet = defineFacet<DropIndicatorPayload, PluginPayload>({\n parent: pluginFacet,\n singleton: true,\n reducer: (payloads: DropIndicatorPayload[]): PluginPayload => {\n let showHandlers = payloads.map(p => p.onShow).filter(x => !!x)\n let hideHandlers = payloads.map(p => p.onHide).filter(x => !!x)\n let dragHandlers = payloads.map(p => p.onDrag).filter(x => !!x)\n\n let showHandler: ShowHandler = (options) => {\n for (let fn of showHandlers) {\n fn(options)\n }\n }\n\n let hideHandler: VoidFunction = () => {\n for (let fn of hideHandlers) {\n fn()\n }\n }\n\n let dragHandler: DragEventHandler = (options): boolean => {\n for (let fn of dragHandlers) {\n if (fn(options) === false) return false\n }\n return true\n }\n\n if (showHandlers.length === 0) {\n // No `onShow` event handler, so we don't need to create a plugin.\n return []\n }\n\n return createDropIndicatorPlugin({\n onDrag: dragHandler,\n onShow: showHandler,\n onHide: hideHandler,\n })\n },\n})\n","import type { PlainExtension } from '@prosekit/core'\n\nimport {\n defineDropIndicatorPayload,\n type DropIndicatorPayload,\n} from './drop-indicator-facet'\n\n/**\n * @internal\n */\nexport type DropIndicatorExtension = PlainExtension\n\n/**\n * Defines an extension that controls the behavior of the drop indicator.\n *\n * This extension itself doesn't draw the drop indicator, but it provides the\n * necessary callbacks to do so. You probably don't want to use this extension\n * directly, but rather use the `<DropIndicator>` component.\n *\n * You can add this extension multiple times. If any extension has `onDrag`\n * callback defined, and it returns `false`, then the drop point will be\n * discarded.\n *\n * @public\n */\nexport function defineDropIndicator(\n options?: DropIndicatorOptions,\n): DropIndicatorExtension {\n return defineDropIndicatorPayload(options ?? {})\n}\n\n/**\n * Options for {@link defineDropIndicator}.\n *\n * @public\n */\nexport interface DropIndicatorOptions extends DropIndicatorPayload {}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"enter-rule-RdhEA900.js","names":["rule: EnterRule","regex: RegExp","handler: EnterRuleHandler","stop: boolean","rules: EnterRule[]","command: Command"],"sources":["../src/enter-rule/index.ts"],"sourcesContent":["import {\n defineFacet,\n defineFacetPayload,\n getNodeType,\n isTextSelection,\n maybeRun,\n OBJECT_REPLACEMENT_CHARACTER,\n pluginFacet,\n type PlainExtension,\n type PluginPayload,\n} from '@prosekit/core'\nimport { keydownHandler } from '@prosekit/pm/keymap'\nimport type {\n Attrs,\n NodeType,\n} from '@prosekit/pm/model'\nimport {\n PluginKey,\n ProseMirrorPlugin,\n type Command,\n type EditorState,\n type Transaction,\n} from '@prosekit/pm/state'\nimport type { EditorView } from '@prosekit/pm/view'\n\n/**\n * @public\n *\n * Options for {@link EnterRuleHandler}.\n */\nexport interface EnterRuleHandlerOptions {\n /**\n * The current editor state.\n */\n state: EditorState\n\n /**\n * The start position of the matched text.\n */\n from: number\n\n /**\n * The end position of the matched text.\n */\n to: number\n\n /**\n * The matched result from the regular expression.\n */\n match: RegExpExecArray\n}\n\n/**\n * @public\n */\nexport type EnterRuleHandler = (options: EnterRuleHandlerOptions) => Transaction | null\n\n/**\n * Options for {@link defineEnterRule}.\n *\n * @public\n */\nexport type EnterRuleOptions = {\n /**\n * The regular expression to match against. It should end with `$`.\n */\n regex: RegExp\n\n /**\n * A function to be called when an enter rule is triggered.\n */\n handler: EnterRuleHandler\n\n /**\n * Whether to stop further handlers from being called if this rule is triggered.\n *\n * @default false\n */\n stop?: boolean\n}\n\n/**\n * Options for {@link defineTextBlockEnterRule}.\n *\n * @public\n */\nexport interface TextBlockEnterRuleOptions {\n /**\n * The regular expression to match against. It should end with `$`.\n */\n regex: RegExp\n\n /**\n * The node type to replace the matched text with.\n */\n type: string | NodeType\n\n /**\n * Attributes to set on the node. If a function is provided, it will be called\n * with the matched result from the regular expression.\n */\n attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)\n\n /**\n * Whether to stop further handlers from being called if this rule is triggered.\n *\n * @default true\n */\n stop?: boolean\n}\n\n/**\n * Defines an enter rule. An enter rule applies when the text directly in front of\n * the cursor matches `regex` and user presses Enter. The `regex` should end\n * with `$`.\n *\n * @param options\n *\n * @public\n */\nexport function defineEnterRule({\n regex,\n handler,\n stop = false,\n}: EnterRuleOptions): PlainExtension {\n const rule: EnterRule = new EnterRule(regex, handler, stop)\n return defineFacetPayload(enterRule, [rule]) as PlainExtension\n}\n\n/**\n * Defines an enter rule that replaces the matched text with a block node.\n *\n * See also {@link defineEnterRule}.\n *\n * @param options\n *\n * @public\n */\nexport function defineTextBlockEnterRule({\n regex,\n type,\n attrs,\n stop = true,\n}: TextBlockEnterRuleOptions): PlainExtension {\n return defineEnterRule({\n regex,\n handler: ({ state, from, to, match }) => {\n const nodeType = getNodeType(state.schema, type)\n const $start = state.doc.resolve(from)\n\n if (\n !$start\n .node(-1)\n .canReplaceWith($start.index(-1), $start.indexAfter(-1), nodeType)\n ) {\n return null\n }\n\n const nodeAttrs = maybeRun(attrs, match)\n return state.tr\n .delete(from, to)\n .setBlockType(from, from, nodeType, nodeAttrs)\n },\n stop,\n })\n}\n\n/**\n * @internal\n */\nclass EnterRule {\n constructor(\n readonly regex: RegExp,\n readonly handler: EnterRuleHandler,\n readonly stop: boolean,\n ) {}\n}\n\nconst enterRule = defineFacet<EnterRule, PluginPayload>({\n reduce: () => {\n let rules: EnterRule[] = []\n\n const command: Command = (state, dispatch, view) => {\n if (!view) return false\n return execRules(view, rules, dispatch)\n }\n const handler = keydownHandler({ Enter: command })\n const plugin = new ProseMirrorPlugin({\n key: new PluginKey('prosekit-enter-rule'),\n props: { handleKeyDown: handler },\n })\n\n return function reducer(inputs) {\n rules = inputs\n return plugin\n }\n },\n\n parent: pluginFacet,\n})\n\nfunction execRules(\n view: EditorView,\n rules: readonly EnterRule[],\n dispatch?: (tr: Transaction) => void,\n): boolean {\n if (view.composing) return false\n const state = view.state\n const selection = state.selection\n if (!isTextSelection(selection)) return false\n const $cursor = selection.$cursor\n if (!$cursor || $cursor.parent.type.spec.code) return false\n\n const textBefore = $cursor.parent.textBetween(\n Math.max(0, $cursor.parentOffset - MAX_MATCH),\n $cursor.parentOffset,\n null,\n OBJECT_REPLACEMENT_CHARACTER,\n )\n\n for (const rule of rules) {\n rule.regex.lastIndex = 0\n const match = rule.regex.exec(textBefore)\n const tr = match\n && rule.handler({\n state,\n from: $cursor.pos - match[0].length,\n to: $cursor.pos,\n match,\n })\n if (!tr) continue\n dispatch?.(tr)\n\n if (rule.stop) {\n return true\n }\n }\n return false\n}\n\nconst MAX_MATCH = 200\n"],"mappings":";;;;;;;;;;;;;;AAwHA,SAAgB,gBAAgB,EAC9B,OACA,SACA,OAAO,SAC4B;CACnC,MAAMA,OAAkB,IAAI,UAAU,OAAO,SAAS,KAAK;AAC3D,QAAO,mBAAmB,WAAW,CAAC,KAAK,CAAC;;;;;;;;;;;AAY9C,SAAgB,yBAAyB,EACvC,OACA,MACA,OACA,OAAO,QACqC;AAC5C,QAAO,gBAAgB;EACrB;EACA,UAAU,EAAE,OAAO,MAAM,IAAI,YAAY;GACvC,MAAM,WAAW,YAAY,MAAM,QAAQ,KAAK;GAChD,MAAM,SAAS,MAAM,IAAI,QAAQ,KAAK;AAEtC,OACE,CAAC,OACE,KAAK,GAAG,CACR,eAAe,OAAO,MAAM,GAAG,EAAE,OAAO,WAAW,GAAG,EAAE,SAAS,CAEpE,QAAO;GAGT,MAAM,YAAY,SAAS,OAAO,MAAM;AACxC,UAAO,MAAM,GACV,OAAO,MAAM,GAAG,CAChB,aAAa,MAAM,MAAM,UAAU,UAAU;;EAElD;EACD,CAAC;;;;;AAMJ,IAAM,YAAN,MAAgB;CACd,YACE,AAASC,OACT,AAASC,SACT,AAASC,MACT;EAHS;EACA;EACA;;;AAIb,MAAM,YAAY,YAAsC;CACtD,cAAc;EACZ,IAAIC,QAAqB,EAAE;EAE3B,MAAMC,WAAoB,OAAO,UAAU,SAAS;AAClD,OAAI,CAAC,KAAM,QAAO;AAClB,UAAO,UAAU,MAAM,OAAO,SAAS;;EAEzC,MAAM,UAAU,eAAe,EAAE,OAAO,SAAS,CAAC;EAClD,MAAM,SAAS,IAAI,kBAAkB;GACnC,KAAK,IAAI,UAAU,sBAAsB;GACzC,OAAO,EAAE,eAAe,SAAS;GAClC,CAAC;AAEF,SAAO,SAAS,QAAQ,QAAQ;AAC9B,WAAQ;AACR,UAAO;;;CAIX,QAAQ;CACT,CAAC;AAEF,SAAS,UACP,MACA,OACA,UACS;AACT,KAAI,KAAK,UAAW,QAAO;CAC3B,MAAM,QAAQ,KAAK;CACnB,MAAM,YAAY,MAAM;AACxB,KAAI,CAAC,gBAAgB,UAAU,CAAE,QAAO;CACxC,MAAM,UAAU,UAAU;AAC1B,KAAI,CAAC,WAAW,QAAQ,OAAO,KAAK,KAAK,KAAM,QAAO;CAEtD,MAAM,aAAa,QAAQ,OAAO,YAChC,KAAK,IAAI,GAAG,QAAQ,eAAe,UAAU,EAC7C,QAAQ,cACR,MACA,6BACD;AAED,MAAK,MAAM,QAAQ,OAAO;AACxB,OAAK,MAAM,YAAY;EACvB,MAAM,QAAQ,KAAK,MAAM,KAAK,WAAW;EACzC,MAAM,KAAK,SACN,KAAK,QAAQ;GACd;GACA,MAAM,QAAQ,MAAM,MAAM,GAAG;GAC7B,IAAI,QAAQ;GACZ;GACD,CAAC;AACJ,MAAI,CAAC,GAAI;AACT,aAAW,GAAG;AAEd,MAAI,KAAK,KACP,QAAO;;AAGX,QAAO;;AAGT,MAAM,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"input-rule-B17tpW4m.js","names":["rules: InputRule[]"],"sources":["../src/input-rule/index.ts"],"sourcesContent":["import {\n defineFacet,\n defineFacetPayload,\n getMarkType,\n getNodeType,\n isMarkAbsent,\n maybeRun,\n pluginFacet,\n type PlainExtension,\n type PluginPayload,\n} from '@prosekit/core'\nimport {\n InputRule,\n inputRules,\n textblockTypeInputRule,\n wrappingInputRule,\n} from '@prosekit/pm/inputrules'\nimport type {\n Attrs,\n MarkType,\n NodeType,\n ProseMirrorNode,\n Schema,\n} from '@prosekit/pm/model'\nimport type { Plugin } from '@prosekit/pm/state'\n\n/**\n * Defines an input rule extension.\n *\n * @param rule - The ProseMirror input rule to add.\n *\n * @public\n */\nexport function defineInputRule(rule: InputRule): PlainExtension {\n return defineInputRuleFacetPayload(() => rule)\n}\n\n/**\n * Options for {@link defineMarkInputRule}.\n *\n * @public\n */\nexport interface MarkInputRuleOptions {\n /**\n * The regular expression to match against, which should end with `$` and has\n * exactly one capture group. All other matched text outside the capture group\n * will be deleted.\n */\n regex: RegExp\n\n /**\n * The type of mark to set.\n */\n type: string | MarkType\n\n /**\n * Attributes to set on the mark.\n */\n attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)\n\n /**\n * Whether this rule should fire inside marks marked as [code](https://prosemirror.net/docs/ref/#model.MarkSpec.code).\n *\n * @default `false`\n */\n inCodeMark?: boolean\n}\n\n/**\n * @internal\n */\nexport function createMarkInputRule({\n regex,\n type,\n attrs = null,\n inCodeMark = false,\n}: MarkInputRuleOptions): InputRule {\n const rule = new InputRule(regex, (state, match, start, end) => {\n const { tr, schema } = state\n const [fullText, markText] = match\n\n if (!markText) {\n return null\n }\n\n const markStart = start + fullText.indexOf(markText)\n const markEnd = markStart + markText.length\n\n if (!(start <= markStart && markStart < markEnd && markEnd <= end)) {\n // Incorrect regex.\n return null\n }\n\n const markType = getMarkType(schema, type)\n const mark = markType.create(maybeRun(attrs, match))\n\n if (!isMarkAbsent(tr.doc, markStart, markEnd, markType, attrs)) {\n // The mark is already active.\n return null\n }\n\n const initialStoredMarks = tr.storedMarks ?? []\n\n tr.addMark(markStart, markEnd, mark)\n\n if (markEnd < end) {\n tr.delete(markEnd, end)\n }\n if (start < markStart) {\n tr.delete(start, markStart)\n }\n\n // Make sure not to reactivate any marks which had previously been\n // deactivated. By keeping track of the initial stored marks we are able to\n // discard any unintended consequences of deleting text and adding it again.\n tr.setStoredMarks(initialStoredMarks)\n\n return tr\n }, { inCodeMark })\n\n return rule\n}\n\n/**\n * Defines an input rule for automatically adding inline marks when a given\n * pattern is typed.\n *\n * @public\n */\nexport function defineMarkInputRule(\n options: MarkInputRuleOptions,\n): PlainExtension {\n return defineInputRule(createMarkInputRule(options))\n}\n\n/**\n * Defines an input rule that changes the type of a textblock when the matched\n * text is typed into it.\n *\n * See also [textblockTypeInputRule](https://prosemirror.net/docs/ref/#inputrules.textblockTypeInputRule)\n *\n * @param options\n *\n * @public\n */\nexport function defineTextBlockInputRule({\n regex,\n type,\n attrs,\n}: {\n /**\n * The regular expression to match against, which should end with `$`. It\n * usually also starts with `^` to that it is only matched at the start of a\n * textblock.\n */\n regex: RegExp\n\n /**\n * The node type to replace the matched text with.\n */\n type: string | NodeType\n\n /**\n * Attributes to set on the node.\n */\n attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)\n}): PlainExtension {\n return defineInputRuleFacetPayload(({ schema }): InputRule => {\n const nodeType = getNodeType(schema, type)\n return textblockTypeInputRule(regex, nodeType, attrs)\n })\n}\n\n/**\n * Defines an input rule for automatically wrapping a textblock when a given\n * string is typed.\n *\n * See also [wrappingInputRule](https://prosemirror.net/docs/ref/#inputrules.wrappingInputRule)\n *\n * @param options\n *\n * @public\n */\nexport function defineWrappingInputRule({\n regex,\n type,\n attrs,\n join,\n}: {\n /**\n * The regular expression to match against, which should end with `$`. It\n * usually also starts with `^` to that it is only matched at the start of a\n * textblock.\n */\n regex: RegExp\n\n /**\n * The type of node to wrap in.\n */\n type: string | NodeType\n\n /**\n * Attributes to set on the node.\n */\n attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)\n\n /**\n * By default, if there's a node with the same type above the newly wrapped\n * node, the rule will try to\n * [join](https://prosemirror.net/docs/ref/#transform.Transform.join) those\n * two nodes. You can pass a join predicate, which takes a regular expression\n * match and the node before the wrapped node, and can return a boolean to\n * indicate whether a join should happen.\n */\n join?: (match: RegExpMatchArray, node: ProseMirrorNode) => boolean\n}): PlainExtension {\n return defineInputRuleFacetPayload(({ schema }): InputRule => {\n const nodeType = getNodeType(schema, type)\n return wrappingInputRule(regex, nodeType, attrs, join)\n })\n}\n\nfunction defineInputRuleFacetPayload(input: InputRulePayload): PlainExtension {\n return defineFacetPayload(inputRuleFacet, [input]) as PlainExtension\n}\n\ntype InputRulePayload = (context: { schema: Schema }) => InputRule\n\nconst inputRuleFacet = defineFacet<InputRulePayload, PluginPayload>({\n reducer: (inputs: InputRulePayload[]): PluginPayload => {\n return (context): Plugin[] => {\n const rules: InputRule[] = inputs.flatMap((callback) => callback(context))\n return [inputRules({ rules })]\n }\n },\n parent: pluginFacet,\n})\n"],"mappings":";;;;;;;;;;;AAiCA,SAAgB,gBAAgB,MAAiC;AAC/D,QAAO,kCAAkC,KAAK;;;;;AAqChD,SAAgB,oBAAoB,EAClC,OACA,MACA,QAAQ,MACR,aAAa,SACqB;AA4ClC,QA3Ca,IAAI,UAAU,QAAQ,OAAO,OAAO,OAAO,QAAQ;EAC9D,MAAM,EAAE,IAAI,WAAW;EACvB,MAAM,CAAC,UAAU,YAAY;AAE7B,MAAI,CAAC,SACH,QAAO;EAGT,MAAM,YAAY,QAAQ,SAAS,QAAQ,SAAS;EACpD,MAAM,UAAU,YAAY,SAAS;AAErC,MAAI,EAAE,SAAS,aAAa,YAAY,WAAW,WAAW,KAE5D,QAAO;EAGT,MAAM,WAAW,YAAY,QAAQ,KAAK;EAC1C,MAAM,OAAO,SAAS,OAAO,SAAS,OAAO,MAAM,CAAC;AAEpD,MAAI,CAAC,aAAa,GAAG,KAAK,WAAW,SAAS,UAAU,MAAM,CAE5D,QAAO;EAGT,MAAM,qBAAqB,GAAG,eAAe,EAAE;AAE/C,KAAG,QAAQ,WAAW,SAAS,KAAK;AAEpC,MAAI,UAAU,IACZ,IAAG,OAAO,SAAS,IAAI;AAEzB,MAAI,QAAQ,UACV,IAAG,OAAO,OAAO,UAAU;AAM7B,KAAG,eAAe,mBAAmB;AAErC,SAAO;IACN,EAAE,YAAY,CAAC;;;;;;;;AAWpB,SAAgB,oBACd,SACgB;AAChB,QAAO,gBAAgB,oBAAoB,QAAQ,CAAC;;;;;;;;;;;;AAatD,SAAgB,yBAAyB,EACvC,OACA,MACA,SAkBiB;AACjB,QAAO,6BAA6B,EAAE,aAAwB;EAC5D,MAAM,WAAW,YAAY,QAAQ,KAAK;AAC1C,SAAO,uBAAuB,OAAO,UAAU,MAAM;GACrD;;;;;;;;;;;;AAaJ,SAAgB,wBAAwB,EACtC,OACA,MACA,OACA,QA4BiB;AACjB,QAAO,6BAA6B,EAAE,aAAwB;EAC5D,MAAM,WAAW,YAAY,QAAQ,KAAK;AAC1C,SAAO,kBAAkB,OAAO,UAAU,OAAO,KAAK;GACtD;;AAGJ,SAAS,4BAA4B,OAAyC;AAC5E,QAAO,mBAAmB,gBAAgB,CAAC,MAAM,CAAC;;AAKpD,MAAM,iBAAiB,YAA6C;CAClE,UAAU,WAA8C;AACtD,UAAQ,YAAsB;GAC5B,MAAMA,QAAqB,OAAO,SAAS,aAAa,SAAS,QAAQ,CAAC;AAC1E,UAAO,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;;;CAGlC,QAAQ;CACT,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"prosekit-extensions-file.d.ts","names":[],"sources":["../src/file/file-drop-handler.ts","../src/file/file-paste-handler.ts","../src/file/file-upload.ts"],"sourcesContent":[],"mappings":";;;;UAYiB,sBAAA;;AAAjB;;MAIQ,EAAA,UAAA;;;;EAwBI,KAAA,EAnBH,SAmBG;EAII;;;MAEb,EApBK,IAoBL;EAAc;;;;AClCjB;;;;;;AAuBA;AAIgB,KDCJ,eAAA,GCD0B,CAAA,OAAA,EDE3B,sBCF2B,EAAA,GAAA,OAAA,GAAA,IAAA;AAAA,iBDKtB,qBAAA,CCLsB,OAAA,EDM3B,eCN2B,CAAA,EDOnC,cCPmC;;;UA3BrB,uBAAA;;ADAjB;;MAIQ,ECAA,UDAA;;;;EAwBI,KAAA,ECnBH,cDmBkB;EAIX;;;MAEb,ECpBK,IDoBL;;;;;AClCH;;;AASS,KAcG,gBAAA,GAdH,CAAA,OAAA,EAeE,uBAfF,EAAA,GAAA,OAAA,GAAA,IAAA;AAKD,iBAaQ,sBAAA,CAbR,OAAA,EAcG,gBAdH,CAAA,EAeL,cAfK;;;;;;UCvBS,cAAA;EFSA,MAAA,EAAA,MAAA;EAAsB,KAAA,EAAA,MAAA;;AAS9B,UETQ,eAAA,CFSR;;;AAmBT;EAIgB,IAAA,EE5BR,IF4BQ;EAAqB;;;EAEpB,UAAA,EAAA,CAAA,QAAA,EEzBQ,cFyBR,EAAA,GAAA,IAAA;;;;AClCjB;;AAIQ,KCYI,QDZJ,CAAA,MAAA,CAAA,GAAA,CAAA,OAAA,ECYiC,eDZjC,EAAA,GCYqD,ODZrD,CCY6D,MDZ7D,CAAA;;;;AAmBI,cCFC,UDEe,CAAA,MACjB,CAAA,CAAA;EAGK;;;;;;;;ACpChB;EASiB,UAAA,IAAA,EAAA,OAAe;EAAA;;;EASO,SAAA,QAAA,EA4BlB,OA5BkB,CA4BV,MA5BU,CAAA;EAO3B,QAAA,WAAQ;EAAA;;;;;AAKpB;EAAuB,WAAA,CAAA;IAAA,IAAA;IAAA;GAAA,EAAA;IAgBM,IAAA,EAUa,IAVb;IAAR,QAAA,EAUqC,QAVrC,CAU8C,MAV9C,CAAA;;;;;mBAUqC,CAAA,QAAA,EAAA,CAAA,QAAA,EAgCjC,cAhCiC,EAAA,GAAA,IAAA,CAAA,EAiCrD,YAjCqD;;;;SA+CrD,GAAA,CAAA,SAAA,OAAA,CAAA,CAAA,SAAA,EAAA,MAAA,CAAA,EAAA,UAAA,CAAW,MAAX,CAAA,GAAA,SAAA;EAAU"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"prosekit-extensions-file.js","names":["getFiles","facet","getFiles","dropHandler: DropHandler","pasteHandler: PasteHandler"],"sources":["../src/file/helpers.ts","../src/file/file-drop-handler.ts","../src/file/file-paste-handler.ts","../src/file/file-upload.ts"],"sourcesContent":["import type { EditorView } from '@prosekit/pm/view'\n\ntype FileHandler<E extends Event> = (options: {\n view: EditorView\n event: E\n file: File\n}) => boolean | void\n\nfunction handleFile<E extends Event>(\n view: EditorView,\n event: E,\n file: File,\n handlers: FileHandler<E>[],\n) {\n // The last item in `handlers` should has the highest priority.\n for (let i = handlers.length - 1; i >= 0; i--) {\n const handler = handlers[i]\n if (handler({ view, event, file })) {\n return true\n }\n }\n return false\n}\n\nexport function handleEvent<E extends Event>(\n view: EditorView,\n event: E,\n handlers: FileHandler<E>[],\n getFiles: (event: E) => File[],\n): boolean {\n const files = getFiles(event)\n let handled = false\n for (const file of files) {\n if (handleFile(view, event, file, handlers)) {\n handled = true\n }\n }\n return handled\n}\n","import {\n defineFacet,\n defineFacetPayload,\n editorEventFacet,\n type DropHandler,\n type EditorEventPayload,\n type PlainExtension,\n} from '@prosekit/core'\nimport type { EditorView } from '@prosekit/pm/view'\n\nimport { handleEvent } from './helpers'\n\nexport interface FileDropHandlerOptions {\n /**\n * The editor view.\n */\n view: EditorView\n\n /**\n * The event that triggered the drop.\n */\n event: DragEvent\n\n /**\n * The file that was dropped.\n */\n file: File\n\n /**\n * The position of the document where the file was dropped.\n */\n pos: number\n}\n\n/**\n * A function that handles one of the files in a drop event.\n *\n * Returns `true` if the file was handled and thus should not be handled by\n * other handlers.\n */\nexport type FileDropHandler = (\n options: FileDropHandlerOptions,\n) => boolean | void\n\nexport function defineFileDropHandler(\n handler: FileDropHandler,\n): PlainExtension {\n return defineFacetPayload(facet, [handler]) as PlainExtension\n}\n\nfunction getFiles(event: DragEvent) {\n return Array.from(event.dataTransfer?.files ?? [])\n}\n\nconst facet = defineFacet<FileDropHandler, EditorEventPayload>({\n parent: editorEventFacet,\n singleton: true,\n reducer: (handlers: FileDropHandler[]): EditorEventPayload => {\n const dropHandler: DropHandler = (view, event): boolean => {\n const position = view.posAtCoords({ left: event.x, top: event.y })\n if (!position) {\n return false\n }\n const pos = position.inside > 0 ? position.inside : position.pos\n\n return handleEvent<DragEvent>(\n view,\n event,\n handlers.map((handler) => (options) => handler({ ...options, pos })),\n getFiles,\n )\n }\n return ['drop', dropHandler]\n },\n})\n","import {\n defineFacet,\n defineFacetPayload,\n editorEventFacet,\n type EditorEventPayload,\n type PasteHandler,\n type PlainExtension,\n} from '@prosekit/core'\nimport type { EditorView } from '@prosekit/pm/view'\n\nimport { handleEvent } from './helpers'\n\nexport interface FilePasteHandlerOptions {\n /**\n * The editor view.\n */\n view: EditorView\n\n /**\n * The event that triggered the paste.\n */\n event: ClipboardEvent\n\n /**\n * The file that was pasted.\n */\n file: File\n}\n\n/**\n * A function that handles one of the files in a paste event.\n *\n * Returns `true` if the file was handled and thus should not be handled by\n * other handlers.\n */\nexport type FilePasteHandler = (\n options: FilePasteHandlerOptions,\n) => boolean | void\n\nexport function defineFilePasteHandler(\n handler: FilePasteHandler,\n): PlainExtension {\n return defineFacetPayload(facet, [handler]) as PlainExtension\n}\n\nfunction getFiles(event: ClipboardEvent) {\n return Array.from(event.clipboardData?.files ?? [])\n}\n\nconst facet = defineFacet<FilePasteHandler, EditorEventPayload>({\n parent: editorEventFacet,\n singleton: true,\n reducer: (handlers: FilePasteHandler[]): EditorEventPayload => {\n const pasteHandler: PasteHandler = (view, event): boolean => {\n return handleEvent<ClipboardEvent>(view, event, handlers, getFiles)\n }\n return ['paste', pasteHandler]\n },\n})\n","/**\n * An interface representing the upload progress.\n */\nexport interface UploadProgress {\n // A number representing the amount of work already performed by the\n // underlying process.\n loaded: number\n // A number representing the total amount of work that the underlying\n // process is in the progress of performing.\n total: number\n}\n\nexport interface UploaderOptions {\n /**\n * The file to be uploaded.\n */\n file: File\n\n /**\n * A callback function that should be called with the upload progress updates.\n */\n onProgress: (progress: UploadProgress) => void\n}\n\n/**\n * The implementation of the actual upload function. You need to implement this\n * function to upload files to your desired destination.\n */\nexport type Uploader<Result> = (options: UploaderOptions) => Promise<Result>\n\n/**\n * A class that represents a upload task.\n */\nexport class UploadTask<Result> {\n /**\n * An [object URL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL)\n * representing the file to be uploaded. This URL will be revoked once the\n * upload is complete successfully.\n */\n readonly objectURL: string\n\n /**\n * A boolean indicating whether the upload is complete (either successfully or with an error).\n */\n protected done = false\n\n /**\n * A promise that fulfills once the upload is complete, or rejects if an error occurs.\n */\n readonly finished: Promise<Result>\n\n private subscribers: ((progress: UploadProgress) => void)[] = []\n\n /**\n * Creates a new upload task. You can find the upload task by its object URL\n * later using `UploadTask.get()`.\n *\n * @param options - The options for the upload task.\n */\n constructor({ file, uploader }: { file: File; uploader: Uploader<Result> }) {\n this.objectURL = URL.createObjectURL(file)\n this.finished = new Promise((resolve, reject) => {\n const maybePromise = uploader({\n file,\n onProgress: (progress) => {\n for (const subscriber of this.subscribers) {\n subscriber(progress)\n }\n },\n })\n Promise.resolve(maybePromise).then(\n (result) => {\n this.done = true\n URL.revokeObjectURL(this.objectURL)\n resolve(result)\n },\n (error) => {\n this.done = true\n reject(\n new Error('[prosekit] Failed to upload file', { cause: error }),\n )\n },\n )\n })\n store.set(this.objectURL, this)\n }\n\n /**\n * Subscribes to progress updates. Returns a function to unsubscribe.\n */\n public subscribeProgress(\n callback: (progress: UploadProgress) => void,\n ): VoidFunction {\n this.subscribers.push(callback)\n return () => {\n this.subscribers = this.subscribers.filter(\n (subscriber) => subscriber !== callback,\n )\n }\n }\n\n /**\n * Finds an upload task by its object URL.\n */\n static get<Result = unknown>(\n objectURL: string,\n ): UploadTask<Result> | undefined {\n return store.get(objectURL) as UploadTask<Result> | undefined\n }\n\n /**\n * Deletes an upload task by its object URL.\n */\n static delete(objectURL: string): void {\n store.delete(objectURL)\n }\n}\n\nconst store = new Map<string, UploadTask<unknown>>()\n"],"mappings":";;;AAQA,SAAS,WACP,MACA,OACA,MACA,UACA;AAEA,MAAK,IAAI,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;EAC7C,MAAM,UAAU,SAAS;AACzB,MAAI,QAAQ;GAAE;GAAM;GAAO;GAAM,CAAC,CAChC,QAAO;;AAGX,QAAO;;AAGT,SAAgB,YACd,MACA,OACA,UACA,YACS;CACT,MAAM,QAAQA,WAAS,MAAM;CAC7B,IAAI,UAAU;AACd,MAAK,MAAM,QAAQ,MACjB,KAAI,WAAW,MAAM,OAAO,MAAM,SAAS,CACzC,WAAU;AAGd,QAAO;;;;;ACOT,SAAgB,sBACd,SACgB;AAChB,QAAO,mBAAmBC,SAAO,CAAC,QAAQ,CAAC;;AAG7C,SAASC,WAAS,OAAkB;AAClC,QAAO,MAAM,KAAK,MAAM,cAAc,SAAS,EAAE,CAAC;;AAGpD,MAAMD,UAAQ,YAAiD;CAC7D,QAAQ;CACR,WAAW;CACX,UAAU,aAAoD;EAC5D,MAAME,eAA4B,MAAM,UAAmB;GACzD,MAAM,WAAW,KAAK,YAAY;IAAE,MAAM,MAAM;IAAG,KAAK,MAAM;IAAG,CAAC;AAClE,OAAI,CAAC,SACH,QAAO;GAET,MAAM,MAAM,SAAS,SAAS,IAAI,SAAS,SAAS,SAAS;AAE7D,UAAO,YACL,MACA,OACA,SAAS,KAAK,aAAa,YAAY,QAAQ;IAAE,GAAG;IAAS;IAAK,CAAC,CAAC,EACpED,WACD;;AAEH,SAAO,CAAC,QAAQ,YAAY;;CAE/B,CAAC;;;;ACnCF,SAAgB,uBACd,SACgB;AAChB,QAAO,mBAAmB,OAAO,CAAC,QAAQ,CAAC;;AAG7C,SAAS,SAAS,OAAuB;AACvC,QAAO,MAAM,KAAK,MAAM,eAAe,SAAS,EAAE,CAAC;;AAGrD,MAAM,QAAQ,YAAkD;CAC9D,QAAQ;CACR,WAAW;CACX,UAAU,aAAqD;EAC7D,MAAME,gBAA8B,MAAM,UAAmB;AAC3D,UAAO,YAA4B,MAAM,OAAO,UAAU,SAAS;;AAErE,SAAO,CAAC,SAAS,aAAa;;CAEjC,CAAC;;;;;;;ACzBF,IAAa,aAAb,MAAgC;;;;;;;CA0B9B,YAAY,EAAE,MAAM,YAAwD;cAf3D;qBAO6C,EAAE;AAS9D,OAAK,YAAY,IAAI,gBAAgB,KAAK;AAC1C,OAAK,WAAW,IAAI,SAAS,SAAS,WAAW;GAC/C,MAAM,eAAe,SAAS;IAC5B;IACA,aAAa,aAAa;AACxB,UAAK,MAAM,cAAc,KAAK,YAC5B,YAAW,SAAS;;IAGzB,CAAC;AACF,WAAQ,QAAQ,aAAa,CAAC,MAC3B,WAAW;AACV,SAAK,OAAO;AACZ,QAAI,gBAAgB,KAAK,UAAU;AACnC,YAAQ,OAAO;OAEhB,UAAU;AACT,SAAK,OAAO;AACZ,WACE,IAAI,MAAM,oCAAoC,EAAE,OAAO,OAAO,CAAC,CAChE;KAEJ;IACD;AACF,QAAM,IAAI,KAAK,WAAW,KAAK;;;;;CAMjC,AAAO,kBACL,UACc;AACd,OAAK,YAAY,KAAK,SAAS;AAC/B,eAAa;AACX,QAAK,cAAc,KAAK,YAAY,QACjC,eAAe,eAAe,SAChC;;;;;;CAOL,OAAO,IACL,WACgC;AAChC,SAAO,MAAM,IAAI,UAAU;;;;;CAM7B,OAAO,OAAO,WAAyB;AACrC,QAAM,OAAO,UAAU;;;AAI3B,MAAM,wBAAQ,IAAI,KAAkC"}